//============================================================================
//  Copyright (c) 1996-2002 Winbond Electronic Corporation
//
//  Module Name:
//    Wb35Tx.c
//
//  Abstract:
//    Processing the Tx message and put into down layer
//
//============================================================================
#include "sysdef.h"


BOOLEAN
Wb35Tx_get_tx_buffer( IN phw_data_t pHwData, IN PUCHAR *pBuffer )
{
	PWB35TX		pWb35Tx = &pHwData->Wb35Tx;

	*pBuffer = pWb35Tx->TxBuffer[0];
	return TRUE;
}

VOID Wb35Tx_start( IN phw_data_t pHwData )
{
	PWB35TX		pWb35Tx = &pHwData->Wb35Tx;

	// Allow only one thread to run into function
	if( OS_ATOMIC_INC( pHwData->Adapter, &pWb35Tx->TxFireCounter ) == 1 )
	{
		pWb35Tx->EP4vm_state = VM_RUNNING;
		Wb35Tx( pHwData );
	}
	else
		OS_ATOMIC_DEC( pHwData->Adapter, &pWb35Tx->TxFireCounter );
}


VOID Wb35Tx( IN phw_data_t pHwData )
{
	PWB35TX		pWb35Tx = &pHwData->Wb35Tx;
	PADAPTER	Adapter = pHwData->Adapter;
	PUCHAR		pTxBufferAddress;
	PMDS		pMds = &Adapter->Mds;
	PURB		pUrb = (PURB)pWb35Tx->Tx4Urb;
	int         retv;
	ULONG		SendIndex;


	do
	{
		if( pHwData->SurpriseRemove || pHwData->HwStop )
			break;

		if( pWb35Tx->tx_halt )
			break;

		//Ownership checking
		SendIndex = pWb35Tx->TxSendIndex;
		if( !pMds->TxOwner[ SendIndex ] )//No more data need to be sent, return immediately
			break;

		pTxBufferAddress = pWb35Tx->TxBuffer[SendIndex];
		#ifdef CONFIG_BOARD_W90N740 //Added by WangJS 2006.9.7
		pTxBufferAddress = (PUCHAR)((ULONG)pTxBufferAddress | 0x80000000);
		#endif
		//
		// Issuing URB
		//
		usb_fill_bulk_urb( pUrb,
						   pHwData->WbUsb.udev,
						   usb_sndbulkpipe(pHwData->WbUsb.udev, 4),
						   pTxBufferAddress,
						   pMds->TxBufferSize[ SendIndex ],
						   Wb35Tx_complete,
						   pHwData);

		pWb35Tx->EP4vm_state = VM_RUNNING;
		retv = wb_usb_submit_urb( pUrb );
		if( retv<0 )
		{
			#ifdef _PE_TX_DUMP_
			WBDEBUG(("EP4 Tx Irp sending error\n"));
			#endif
			break;
		}

		// Check if driver needs issue Irp for EP2
		pWb35Tx->TxFillCount += pMds->TxCountInBuffer[SendIndex];
		#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
		if( pWb35Tx->TxFillCount > 12 ) // Fixed to 12. 20060928
			Wb35Tx_EP2VM_start( pHwData );
		#endif

		pWb35Tx->ByteTransfer += pMds->TxBufferSize[SendIndex];
		return;

	}while(FALSE);

	pWb35Tx->EP4vm_state = VM_STOP;
	OS_ATOMIC_DEC( pHwData->Adapter, &pWb35Tx->TxFireCounter );
}


VOID Wb35Tx_complete( IN PURB pUrb )
{
	phw_data_t	pHwData = pUrb->context;
	PADAPTER	Adapter = (PADAPTER)pHwData->Adapter;
	PWB35TX		pWb35Tx = &pHwData->Wb35Tx;
//	PWBLINUX	pWbLinux = &Adapter->WbLinux;
	PMDS		pMds = &Adapter->Mds;

	// Variable setting
	pWb35Tx->EP4vm_state = VM_COMPLETED;
	pWb35Tx->EP4VM_status = pUrb->status;//Store the last result of Irp
	pMds->TxOwner[ pWb35Tx->TxSendIndex ] = 0;// Set the owner. Free the owner bit always.
	pWb35Tx->TxSendIndex++;
	pWb35Tx->TxSendIndex %= MAX_USB_TX_BUFFER_NUMBER;
	
	do
	{
		if( pHwData->SurpriseRemove || pHwData->HwStop ) // Let WbWlanHalt to handle surprise remove
			break;

		if( pWb35Tx->tx_halt )
			break;

		//The URB is completed, check the result
		if( pWb35Tx->EP4VM_status != 0 )
		{
			#ifdef _PE_USB_STATE_DUMP_
			WBDEBUG(("EP4 IoCompleteRoutine return error\n"));
			DebugUsbdStatusInformation( pWb35Tx->EP4VM_status );
			#endif
			pWb35Tx->EP4vm_state = VM_STOP;
			break; // Exit while(FALSE);
		}

		// Do the next send
		Mds_Tx( Adapter );
		Wb35Tx( pHwData );
	
		return;

	}while(FALSE);

	OS_ATOMIC_DEC( pHwData->Adapter, &pWb35Tx->TxFireCounter );
	pWb35Tx->EP4vm_state = VM_STOP;
}

VOID Wb35Tx_reset_descriptor( IN phw_data_t pHwData )
{
	PWB35TX		pWb35Tx = &pHwData->Wb35Tx;

	pWb35Tx->TxSendIndex = 0;
	pWb35Tx->tx_halt = 0;
}

BOOLEAN Wb35Tx_initial(	IN phw_data_t pHwData )
{
	PWB35TX pWb35Tx = &pHwData->Wb35Tx;

	pWb35Tx->Tx4Urb = wb_usb_alloc_urb(0);
	if( !pWb35Tx->Tx4Urb )
		return FALSE;

	pWb35Tx->Tx2Urb = wb_usb_alloc_urb(0);
	if( !pWb35Tx->Tx2Urb )
	{
		usb_free_urb( pWb35Tx->Tx4Urb );
		return FALSE;
	}

	return TRUE;
}

//======================================================
VOID Wb35Tx_stop( IN phw_data_t pHwData )
{
	PWB35TX pWb35Tx = &pHwData->Wb35Tx;

	// Trying to canceling the Trp of EP2
	#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
	if( pWb35Tx->EP2vm_state == VM_RUNNING )
	#endif
        usb_unlink_urb( pWb35Tx->Tx2Urb ); // Only use unlink, let Wb35Tx_destrot to free them
	#ifdef _PE_TX_DUMP_
	WBDEBUG(("EP2 Tx stop\n"));
	#endif

	// Trying to canceling the Irp of EP4
	if( pWb35Tx->EP4vm_state == VM_RUNNING )
		usb_unlink_urb( pWb35Tx->Tx4Urb ); // Only use unlink, let Wb35Tx_destrot to free them
	#ifdef _PE_TX_DUMP_
	WBDEBUG(("EP4 Tx stop\n"));
	#endif
}

//======================================================
VOID Wb35Tx_destroy( IN phw_data_t pHwData )
{
	PWB35TX pWb35Tx = &pHwData->Wb35Tx;

	// Wait for VM stop
	do{
		OS_SLEEP(10000);  // Delay for waiting function enter 940623.1.a
	}while( (pWb35Tx->EP2vm_state != VM_STOP) && (pWb35Tx->EP4vm_state != VM_STOP) );
	OS_SLEEP(10000);  // Delay for waiting function enter 940623.1.b

	if( pWb35Tx->Tx4Urb )
		usb_free_urb( pWb35Tx->Tx4Urb );

	if( pWb35Tx->Tx2Urb )
		usb_free_urb( pWb35Tx->Tx2Urb );

	#ifdef _PE_TX_DUMP_
	WBDEBUG(("Wb35Tx_destroy OK\n"));
	#endif
}

VOID Wb35Tx_CurrentTime( IN phw_data_t pHwData, IN ULONG TimeCount )
{
	PWB35TX pWb35Tx = &pHwData->Wb35Tx;
	BOOL Trigger = FALSE;

	if( pWb35Tx->TxTimer > TimeCount )
		Trigger = TRUE;
	else if( TimeCount > (pWb35Tx->TxTimer+500) )
		Trigger = TRUE;

	if( Trigger )
	{
		pWb35Tx->TxTimer = TimeCount;
		#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
		Wb35Tx_EP2VM_start( pHwData );
		#endif
	}
}

VOID Wb35Tx_EP2VM_start( IN phw_data_t pHwData )
{
	PWB35TX pWb35Tx = &pHwData->Wb35Tx;

	// Allow only one thread to run into function
	if( OS_ATOMIC_INC( pHwData->Adapter, &pWb35Tx->TxResultCount ) == 1 )
	{
		pWb35Tx->EP2vm_state = VM_RUNNING;
		Wb35Tx_EP2VM( pHwData );
	}
	else
		OS_ATOMIC_DEC( pHwData->Adapter, &pWb35Tx->TxResultCount );
}


VOID Wb35Tx_EP2VM( IN phw_data_t pHwData )
{
    PWB35TX pWb35Tx = &pHwData->Wb35Tx;
    PURB	pUrb = (PURB)pWb35Tx->Tx2Urb;
	PULONG	pltmp = (PULONG)pWb35Tx->EP2_buf;
	int		retv;

	do
	{
		if( pHwData->SurpriseRemove || pHwData->HwStop )
			break;

		if( pWb35Tx->tx_halt )
			break;

		//
		// Issuing URB
		//
		usb_fill_int_urb( pUrb,
						  pHwData->WbUsb.udev,
						  usb_rcvintpipe(pHwData->WbUsb.udev,2),
						  pltmp,
						  MAX_INTERRUPT_LENGTH,
						  Wb35Tx_EP2VM_complete,
						  pHwData,32);

		pWb35Tx->EP2vm_state = VM_RUNNING;
		retv = wb_usb_submit_urb( pUrb );

		if(retv < 0)
		{
			#ifdef _PE_TX_DUMP_
			WBDEBUG(("EP2 Tx Irp sending error\n"));
			#endif
			break;
		}

		return;

	}while(FALSE);

	pWb35Tx->EP2vm_state = VM_STOP;
	OS_ATOMIC_DEC( pHwData->Adapter, &pWb35Tx->TxResultCount );
}


VOID Wb35Tx_EP2VM_complete( IN PURB pUrb )
{
	phw_data_t	pHwData = pUrb->context;
	T02_DESCRIPTOR	T02, TSTATUS;
	PADAPTER	Adapter = (PADAPTER)pHwData->Adapter;
	PWB35TX		pWb35Tx = &pHwData->Wb35Tx;
	PULONG		pltmp = (PULONG)pWb35Tx->EP2_buf;
	ULONG		i;
	USHORT		InterruptInLength;


	// Variable setting
	pWb35Tx->EP2vm_state = VM_COMPLETED;
	pWb35Tx->EP2VM_status = pUrb->status;

	do
	{
		// For Linux 2.4. Interrupt will always trigger
		if( pHwData->SurpriseRemove || pHwData->HwStop ) // Let WbWlanHalt to handle surprise remove
			break;

		if( pWb35Tx->tx_halt )
			break;

		//The Urb is completed, check the result
		if( pWb35Tx->EP2VM_status != 0 )
		{
			//#ifdef _PE_USB_STATE_DUMP_
			WBDEBUG(("EP2 IoCompleteRoutine return error\n"));
			//DebugUsbdStatusInformation( pWb35Tx->EP2VM_status );
			//#endif
			pWb35Tx->EP2vm_state= VM_STOP;
			break; // Exit while(FALSE);
		}

		// Update the Tx result
		InterruptInLength = pUrb->actual_length;
		// Modify for minimum memory access and DWORD alignment.
		T02.value = cpu_to_le32(pltmp[0]) >> 8; // [31:8] -> [24:0]
		InterruptInLength -= 1;// 20051221.1.c Modify the follow for more stable
		InterruptInLength >>= 2; // InterruptInLength/4
		for( i=1; i<=InterruptInLength; i++ ) // (pltmp[0]&0xff) is Tx result count but HW may error.
		{
			T02.value |= ((cpu_to_le32(pltmp[i]) & 0xff) << 24);
			#if 0	//20061009 marked by anson's endian
			//[WK endian]
			TSTATUS.T02_transmit_fail 	= T02.value & 0x1;
			TSTATUS.T02_transmit_abort	= (T02.value >> 1) & 0x1;
			TSTATUS.T02_out_of_MaxTxMSDULiftTime = (T02.value >> 2) & 0x1;
			TSTATUS.T02_discard_due_to_null_wep_key = (T02.value >> 4) & 0x1;
			TSTATUS.T02_transmit_complete = (T02.value >> 8) & 0x1;
			TSTATUS.T02_RTS_Cnt			= (T02.value >> 16) & 0xf;
			TSTATUS.T02_MPDU_Cnt		= (T02.value >> 20) & 0xf;
			TSTATUS.T02_Tx_PktID		= (T02.value >> 24) & 0xf;
			TSTATUS.T02_IgnoreResult	= (T02.value >> 30) & 0x1;
			TSTATUS.T02_IsLastMpdu		= (T02.value >> 31) & 0x1;
			#endif 

			TSTATUS.value = T02.value;  //20061009 anson's endian
			Mds_SendComplete( Adapter, &TSTATUS );
			T02.value = cpu_to_le32(pltmp[i]) >> 8;
		}

// 20060928
//		// Start to get the next result, if previous Irp return OK
//		pWb35Tx->EP2vm_state = VM_RUNNING;
//
//		#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
//		Wb35Tx_EP2VM( pHwData );
//		#endif

		return;

	}while(FALSE);

	OS_ATOMIC_DEC( pHwData->Adapter, &pWb35Tx->TxResultCount );
	pWb35Tx->EP2vm_state = VM_STOP;
}



//===================================================================================================
//===================================================================================================



